<template>
	<div :prop="scrollPageProp" :change:prop="scrollPage.propChange" id="scrollPage" class="scroll-page" :style="{
	'color': color,
	'background': bgColor}">
		<yb-list ref="list"
		type="scroll"
		:pulldown="{show: true, endText: '已经到最前面了'}"
		:loadmore="{show: true, endText: '已经到最后面了'}"
		@pulldown="pulldown"
		@loadmore="onLoadmore"
		@scrolltoupper="scrolltoupper">
			<div :style="{height: topGap + 'px', width: '100%'}"></div>
			<div id="scrollContent"></div>
			<div :style="{height: bottomGap + 'px', width: '100%'}"></div>
		</yb-list>
		<computed-page ref="computedPage" :pageType="pageType" :fontSize="fontSize" :lineHeight="lineHeight"
			:slide="slide" :topGap="topGap" :bottomGap="bottomGap"></computed-page>
		<div class="loading" v-if="initLoading" :style="{background: bgColor, color: color, 'font-size': fontSize + 'px'}">
			<page-refresh :color="color">正在加载内容</page-refresh>
		</div>
	</div>
</template>

<script>
	import ComputedPage from './computed-page.vue'
	import PageRefresh from './page-refresh.vue'
	export default {
		components: {
			ComputedPage,
			PageRefresh
		},
		props: {
			//字体颜色
			color: {
				type: String,
				default: '#333333'
			},
			//字体大小（单位px）
			fontSize: {
				type: String | Number,
				default: 15
			},
			//背景颜色
			bgColor: {
				type: String,
				default: '#fcd281'
			},
			//翻页方式
			pageType: {
				type: String,
				default: 'scroll'
			},
			//行间距（单位px）
			lineHeight: {
				type: Number | String,
				default: 15
			},
			//页面左右边距（单位px）
			slide: {
				type: Number | String,
				default: 40
			},
			//页面上边距（单位px）
			topGap: {
				type: Number | String,
				default: 10
			},
			//页面下边距（单位px）
			bottomGap: {
				type: Number | String,
				default: 10
			},
			//开启预加载
			enablePreload: {
				type: Boolean,
				default: false
			}
		},
		data() {
			return {
				contents: [],
				pages: [],
				pageTo: 0,
				disablePulldown: false,
				initLoading: true,
			}
		},
		computed: {
			scrollPageProp() {
				return {
					slide: this.slide,
					topGap: this.topGap,
					bottomGap: this.bottomGap,
					fontSize: this.fontSize,
					lineHeight: this.lineHeight,
					contents: JSON.parse(JSON.stringify(this.contents)),
					pages: JSON.parse(JSON.stringify(this.pages)),
					pageTo: this.pageTo
				}
			}
		},
		methods: {
			init(data) {
				this.contents = data.contents;
				this.resetPage(data);
			},
			pulldown (callback) {
				let contentsIndex = this.contents.findIndex(content => content.chapter == this.pages[0].chapter)
				if ( this.contents[contentsIndex].isStart ) {
					callback('end')
				} else {
					this.loadmore({
						chapter: this.pages[0].chapter - 1,
						type: 'prev'
					}, callback)
					this.$refs.list.resetLoadmore()
				}
			},
			scrolltoupper () {
				let contentsIndex = this.contents.findIndex(content => content.chapter == this.pages[0].chapter)
				if ( !this.contents[contentsIndex].isStart ) {
					this.loadmore({
						chapter: this.pages[0].chapter - 1,
						type: 'prev'
					})
					this.$refs.list.resetLoadmore()
				}
			},
			onLoadmore (callback) {
				let contentsIndex = this.contents.findIndex(content => content.chapter == this.pages[this.pages.length - 1].chapter)
				if ( this.contents[contentsIndex].isEnd ) {
					callback('end')
				} else {
					this.loadmore({
						chapter: this.pages[this.pages.length - 1].chapter + 1,
						type: 'next'
					}, callback)
					this.$refs.list.resetPulldown()
				}
			},
			//重绘页面
			resetPage (data) {
				this.pages = [];
				setTimeout(() => {
					//一次最多渲染3章的内容，根据定位的章节剪切出3章内容渲染
					const nowIndex = this.contents.findIndex(item => item.chapter == data.currentChapter);
					let prevIndex = -1;
					let nextIndex = -1;
					let contents = [];
					if ( !this.contents[nowIndex].isStart ) prevIndex = this.contents.findIndex(item => item.chapter == data.currentChapter - 1);
					if ( !this.contents[nowIndex].isEnd ) nextIndex = this.contents.findIndex(item => item.chapter == data.currentChapter + 1);
					if ( prevIndex > -1 ) {
						contents.push(this.contents[prevIndex])
					}
					contents.push(this.contents[nowIndex])
					if ( nextIndex > -1 ) {
						contents.push(this.contents[nextIndex])
					}
					
					let arr = [];
					const dowhile = (i) => {
						let item = contents[i];
						this.computedChapter(item).then(pages => {
							if (data.currentChapter == item.chapter) {
								let index = Object.keys(pages).findIndex(key => data.start >= pages[key].start &&
									data.start < pages[key].end)
								pages[index > -1 ? index : 0].init = true;
							}
							arr = arr.concat(pages)
							if (i == contents.length - 1) {
								this.pages = arr;
								this.preload(data.currentChapter);
							} else {
								setTimeout(() => {
									dowhile(i + 1)
								}, 100)
							}
						})
					}
					dowhile(0)
				}, 20)
			},
			//加载更多章节
			loadmore (load, callback) {
				const chapter = load.chapter;
				const type = load.type;
				const contentIndex = this.contents.findIndex(item => item.chapter == chapter);
				if ( contentIndex > -1 ) {
					const data = {
						content: this.contents[contentIndex],
						type: type
					}
					this.computedPage(data);
					this.preload(chapter)
					callback ? callback('success') : null
				} else {
					this.$emit('loadmore', chapter, (status, contents) => {
						if (status == 'success') {
							this.contents = JSON.parse(JSON.stringify(contents))
							const index = this.contents.findIndex(item => item.chapter == chapter)
							const data = {
								content: this.contents[index],
								type: type
							}
							this.computedPage(data);
							this.preload(chapter)
						}
						callback ? callback('success') : null
					})
				}
			},
			//预加载章节
			preload (chapter) {
				if ( !this.enablePreload ) return false
				const nowIndex = this.contents.findIndex(item => item.chapter == chapter);
				let prevIndex = -2;
				let nextIndex = -2;
				let chapters = [];
				if ( !this.contents[nowIndex].isStart ) prevIndex = this.contents.findIndex(item => item.chapter == chapter - 1);
				if ( !this.contents[nowIndex].isEnd ) nextIndex = this.contents.findIndex(item => item.chapter == chapter + 1);
				if ( prevIndex == -1 ) {
					chapters.push(chapter - 1);
				}
				if ( nextIndex == -1 ) {
					chapters.push(chapter + 1);
				}
				if ( chapters.length > 0 ) {
					this.$emit('preload', chapters, (status, contents) => {
						if (status == 'success') {
							this.contents = JSON.parse(JSON.stringify(contents))
						}
					})
				}
			},
			computedChapter(content) {
				return new Promise((resolve) => {
					this.$refs.computedPage.computed({
						content: content.content,
						custom: content.custom,
						chapter: content.chapter
					}).then((pages) => {
						resolve(pages);
					})
				}).catch(() => {
					resolve([])
				})
			},
			computedPage(e) {
				this.computedChapter(e.content).then((pages) => {
					let pagesSync = e.type == 'prev' ? pages.concat(this.pages) : this.pages.concat(pages);
					let arr = [];
					pagesSync.forEach(item => {
						if (arr.indexOf(item.chapter) == -1) arr.push(item.chapter)
					})
					if (arr.length > 3) {
						let reChapter = e.type == 'prev' ? pagesSync[pagesSync.length - 1].chapter : pagesSync[0]
							.chapter;
						this.pages = pagesSync.filter(item => item.chapter != reChapter);
					} else {
						this.pages = pagesSync;
					}
				});
			},
			scrollEnd(e) {
				this.initLoading ? this.initLoading = false : null
				this.$emit('scrollEnd', e);
			},
			resetPulldownStatus() {
				this.pulldownStatus = 'none';
			},
			resetPullupStatus () {
				this.pullupStatus = 'none';
			},
			//自定义点击事件
			customClick (e) {
				this.$emit('customClick', e)
			},
			pagePrev () {
				this.pageTo = 0
				this.$nextTick(function(){
					this.pageTo = -1
				})
			},
			pageNext () {
				this.pageTo = 0
				this.$nextTick(function(){
					this.pageTo = 1
				})
			},
			showToast(e) {
				uni.showToast({
					title: e.title,
					icon: 'none'
				})
			}
		}
	}
</script>
<!-- #ifdef H5 || APP-VUE -->
<script lang="renderjs" type="module" module="scrollPage">
	import Vue from 'vue'
	const TIME_BOUNCE = 200;
	const TIME_WATING = 500;
	let myScrollPageDom
	export default {
		data() {
			return {
				scrollInfo: {
					scrollTop: 0,
					scrollHeight: 0,
					offsetHeight: 0
				},
				oldFirstChild: '',
				pagesSync: []
			}
		},
		mounted() {
			this.initDom.bind(this);
			window.triggerCustomClick = (name, args) => {
				// #ifndef H5
				this.$ownerInstance.callMethod('customClick', {
					name: name,
					args: args
				});
				// #endif
				// #ifdef H5
				this.customClick({
					name: name,
					args: args
				});
				// #endif
			}
			new Vue({
				el: '#scrollContent',
				render: (h) => {
					return h('div', {
						attrs: {
							id: 'scroll-content'
						}
					}, this.pagesSync.map(item => {
						return h('div', {
							class: 'scroll-item scroll-item_' + item.dataId,
							attrs: {
								chapter: item.chapter,
								start: item.start,
								end: item.end,
								type: item.type,
								'data-id': item.dataId
							},
							style: {
								width: '100%',
								'box-sizing': 'border-box',
								padding: '1px ' + this.scrollPageProp.slide + 'px',

							}
						}, item.type == 'text' ? item.text.map((text, i) => {
							return h('p', {
								class: 'scroll-text',
								style: {
									'font-size': this.scrollPageProp.fontSize +
										'px',
									'margin-top': this.scrollPageProp
										.lineHeight + 'px',
									height: (this.scrollPageProp.fontSize +
											4) +
										'px',
									'font-family': '"Microsoft YaHei", 微软雅黑',
									'white-space': 'pre-wrap',
								}
							}, text)
						}) : item.type == 'custom' ? [
							h('div', {
								class: 'custom',
								domProps: {
									innerHTML: item.text
								}
							})
						] : [h('p', {
							class: 'scroll-text',
							style: {
								'text-align': 'center',
								'font-size': '20px',
								padding: '60px 0',
								'font-family': '"Microsoft YaHei", 微软雅黑',
								'font-weight': 'bold'
							}
						}, item.text)])
					}))
				}
			})
			document.getElementsByClassName('uni-scroll-view')[1].addEventListener('scroll', () => {
				window.clearTimeout(this.scrollTimer)
				this.scrollTimer = null
				this.scrollTimer = window.setTimeout(() => {
					let pageInfo = this.computedPageInfo();
					this.triggerMethod('scrollEnd', pageInfo);
					window.clearTimeout(this.scrollTimer)
					this.scrollTimer = null
				}, 100)
			})
		},
		methods: {
			initDom() {
				myScrollPageDom = scrollPage.init(document.getElementById('scrollPage'));
				// 观测更新的数据在 view 层可以直接访问到
				myScrollPageDom.setOption(this.scrollPageProp);
			},
			//参数改变
			propChange(newValue, oldValue = {}) {
				for ( let i in newValue.pages ) {
					if ( !this.diff(newValue.pages[i], oldValue.pages.length > 0 ? oldValue.pages[i] : '')) {
						this.pagesChange(newValue.pages, oldValue.pages);
						break;
					}
				}
				if (newValue.fontSize != oldValue.fontSize) {
					this.triggerResetPage();
				}
				if (newValue.lineHeight != oldValue.lineHeight) {
					this.triggerResetPage();
				}
				if (newValue.slide != oldValue.slide) {
					this.triggerResetPage();
				}
				if (newValue.topGap != oldValue.topGap) {
					this.triggerResetPage();
				}
				if (newValue.bottomGap != oldValue.bottomGap) {
					this.triggerResetPage();
				}
				if (newValue.pageTo != oldValue.pageTo) {
					let pageInfo = this.computedPageInfo();
					let index = this.pagesSync.findIndex(page => page.dataId == pageInfo.dataId)
					if ( newValue.pageTo == -1 ) {
						let newIndex = index - 1
						if ( newIndex >= 0 ) {
							document.getElementsByClassName('scroll-item_' + this.pagesSync[newIndex].dataId)[0].scrollIntoView({behavior: 'smooth'})
						} else {
							document.getElementsByClassName('uni-scroll-view')[1].scrollTop = 0
						}
					}
					if ( newValue.pageTo == 1 ) {
						let newIndex = index + 1
						if ( newIndex < this.pagesSync.length ) {
							document.getElementsByClassName('scroll-item_' + this.pagesSync[newIndex].dataId)[0].scrollIntoView({behavior: 'smooth'})
						} else {
							document.getElementsByClassName('uni-scroll-view')[1].scrollTop = document.getElementsByClassName('uni-scroll-view')[1].scrollHeight
						}
					}
				}
			},
			pagesChange(newValue, oldValue) {
				let pageInfo = oldValue && oldValue.length > 0 ? this.computedPageInfo() : null
				this.pagesSync = JSON.parse(JSON.stringify(newValue));
				this.$nextTick(() => {
					if ( pageInfo ) {
						//下一章
						if ( newValue[newValue.length - 1].chapter > oldValue[oldValue.length - 1].chapter ) {
							let currentDom = document.getElementsByClassName('scroll-item_' + oldValue[oldValue.length - 1].dataId)[0]
							let scrollDom = document.getElementsByClassName('uni-scroll-view')[1]
							let loadmore = document.getElementsByClassName('yb-loadmore')[0]
							scrollDom.scrollTop = currentDom.offsetTop - (scrollDom.offsetHeight - currentDom.offsetHeight) + loadmore.offsetHeight
							currentDom = null
							scrollDom = null
							loadmore = null
						} else {//上一章
							document.getElementsByClassName('uni-scroll-view')[1].scrollTop = document.getElementsByClassName('scroll-item_' + pageInfo.dataId)[0].offsetTop
						}
					} else {
						console.log('init')
						let initIndex = this.pagesSync.findIndex(item => item.init);
						if (initIndex > -1) {
							document.getElementsByClassName('uni-scroll-view')[1].scrollTop = document.getElementsByClassName('scroll-item_' + this.pagesSync[initIndex].dataId)[0].offsetTop
						}
					}
				})
			},
			diff(obj1, obj2){
			    var o1 = obj1 instanceof Object;
			    var o2 = obj2 instanceof Object;
			    // 判断是不是对象
			    if (!o1 || !o2) {
			        return obj1 === obj2;
			    }
			
			    //Object.keys() 返回一个由对象的自身可枚举属性(key值)组成的数组,
			    //例如：数组返回下表：let arr = ["a", "b", "c"];console.log(Object.keys(arr))->0,1,2;
			    if (Object.keys(obj1).length !== Object.keys(obj2).length) {
			        return false;
			    }
			
			    for (var o in obj1) {
			        var t1 = obj1[o] instanceof Object;
			        var t2 = obj2[o] instanceof Object;
			        if (t1 && t2) {
			            return this.diff(obj1[o], obj2[o]);
			        } else if (obj1[o] !== obj2[o]) {
			            return false;
			        }
			    }
			    return true;
			},
			computedPageInfo() {
				const scroll = document.getElementsByClassName('uni-scroll-view')[1]
				const scrollItems = scroll.getElementsByClassName('scroll-item');
				const scrollTop = scroll.scrollTop + this.scrollPageProp.topGap + this.scrollPageProp.bottomGap;
				let dataId = -1;
				for (let i = 0; i < scrollItems.length; i++) {
					let offsetTop = scrollItems[i].offsetTop;
					let offsetBottom = scrollItems[i].offsetTop + scrollItems[i].offsetHeight;
					if (scrollTop >= offsetTop && scrollTop < offsetBottom) {
						dataId = parseInt(scrollItems[i].getAttribute('data-id'))
					}
				}
				if (!dataId) {
					if (scrollTop < scrollItems[0].offsetTop) {
						dataId = parseInt(scrollItems[0].getAttribute('data-id'))
					}
					if (scrollTop > scrollItems[scrollItems.length - 1].offsetTop + scrollItems[scrollItems.length - 1]
						.offsetHeight) {
						dataId = parseInt(scrollItems[scrollItems.length - 1].getAttribute('data-id'))
					}
				}
				let index = this.pagesSync.findIndex(item => item.dataId == dataId);
				let pageInfo = this.pagesSync[index];
				const nowChapters = this.pagesSync.filter(item => item.chapter == pageInfo.chapter)
				const contentIndex = this.scrollPageProp.contents.findIndex(content => content.chapter == pageInfo.chapter)
				if ( this.scrollPageProp.contents[contentIndex].title ) pageInfo.title = this.scrollPageProp.contents[contentIndex].title;
				pageInfo.totalPage = nowChapters.length;
				pageInfo.currentPage = nowChapters.findIndex(item => item.dataId == pageInfo.dataId) + 1;
				return pageInfo
			},
			triggerResetPage () {
				let pageInfo = this.computedPageInfo();
				this.pagesSync = [];
				const data = {
					start: pageInfo.start,
					currentChapter: pageInfo.chapter
				}
				this.triggerMethod('resetPage', data)
			},
			triggerMethod(name, arg) {
				// #ifndef H5
				this.$ownerInstance.callMethod(name, arg);
				// #endif
				// #ifdef H5
				this[name](arg);
				// #endif
			}
		}
	}
</script>
<!-- #endif -->

<style scoped>
	.scroll-page {
		width: 100%;
		height: 100%;
		position: absolute;
		left: 0;
		top: 0;
		box-sizing: border-box;
		overflow-y: hidden;
		display: flex;
		flex-direction: column;
	}
	.loading {
		width: 100%;
		height: 100%;
		position: absolute;
		left: 0;
		top: 0;
		box-sizing: border-box;
		display: flex;
		align-items: center;
		justify-content: center;
		z-index: 2;
	}
</style>
